////////////////////////////////////////////////////////////////////////////////
// This file is part of the Mist compiler.                                    //
//                                                                            //
// Copyright (C) 2009 The Mist project                                        //
//                                                                            //
// For a list of contributors, see the COMMITTERS file.                       //
//                                                                            //
// The Mist compiler is free software: you can redistribute it and/or modify  //
// it under the terms of the GNU General Public License as published by       //
// the Free Software Foundation, either version 3 of the License, or          //
// (at your option) any later version.                                        //
//                                                                            //
// The Mist compiler is distributed in the hope that it will be useful,       //
// but WITHOUT ANY WARRANTY; without even the implied warranty of             //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the              //
// GNU General Public License for more details.                               //
//                                                                            //
// You should have received a copy of the GNU General Public License          //
// in the COPYING file. If not, see <http://www.gnu.org/licenses/>.           //
////////////////////////////////////////////////////////////////////////////////

///
/// \file
/// Contains the implementation of the Node::Scope methods from tree.hpp.
///

#include "tree.hpp"

////////////////////////////////////////////////////////////////////////////////
// Includes                                                                   //
////////////////////////////////////////////////////////////////////////////////

#include "navigator.hpp"
#include "tree.hpp"
#include "nodes.hpp"
#include "symbol.hpp"
#include "parse.hpp"

////////////////////////////////////////////////////////////////////////////////
// FindSymbolVisitor Class                                                    //
////////////////////////////////////////////////////////////////////////////////

/// Visits the nodes of an abstract syntax tree and uses it to build the
/// symbol-table. A visitor in the visitor pattern.
///
class FindSymbolVisitor : public Navigator {
	public:
		
		FindSymbolVisitor(wptr<Symbol> symbolRoot);
		
		VISIT_FUNCTIONS_FROM( CONCRETE_NODES )
	
	private:
		
		void enter_scope(wptr<Symbol> scope);
		void leave_scope();
		wptr<Symbol> current_scope();
		
		wptr<Symbol> _currentScope;
};

////////////////////////////////////////////////////////////////////////////////
// Function Implementations                                                   //
////////////////////////////////////////////////////////////////////////////////

void Node::Scope::set_node(wptr<Node> node) {
	_node = node;
}

void Node::Scope::compute_value() {
	wptr<Node> node = _node;
	while (!node->scope.was_manually_set()) {
		assert(node->parent);
		node = node->parent;
	}
	
	sptr<FindSymbolVisitor> visitor(new FindSymbolVisitor(node->scope._manualScope));
	
	node->accept(visitor);
}

wptr<Symbol> Node::Scope::operator=(wptr<Symbol> scope) {
	assert(state() != COMPUTED);
	
	_manualScope = scope;
	compute_value();
	
	return scope;
}

void Node::Scope::set_automatically(wptr<Symbol> scope) {
	if (state() != COMPUTED)
		set_value(scope);
	_manualScope = wptr<Symbol>();
}

bool Node::Scope::was_manually_set() {
	return (bool)_manualScope;
}

////////////////////////////////////////////////////////////////////////////////

FindSymbolVisitor::FindSymbolVisitor(wptr<Symbol> symbolRoot) : _currentScope(symbolRoot) {
	assert(symbolRoot);
}

void FindSymbolVisitor::visit(wptr<Declarations> node) {
	assert(node);
	
	node->scope.set_automatically(current_scope());
	
	Navigator::visit(node);
}

void FindSymbolVisitor::visit(wptr<AssertionArea> node) {
	assert(node);
	
	node->scope.set_automatically(current_scope());
	
	Navigator::visit(node);
}

void FindSymbolVisitor::visit(wptr<IfStatement> node) {
	assert(node);
	
	node->scope.set_automatically(current_scope());
	
	node->condition->accept(wptr_this());
	
	{
		if (typeid(*node->thenStatement) == typeid(CompoundStatement)) {
			node->thenStatement->accept(wptr_this());
		} else {
			enter_scope(Symbol::construct_scope(current_scope()));
				node->thenStatement->accept(wptr_this());
			leave_scope();
		}
	}
	
	if (node->elseStatement) {
		if (typeid(*node->elseStatement) == typeid(CompoundStatement)) {
			node->elseStatement->accept(wptr_this());
		} else {
			enter_scope(Symbol::construct_scope(current_scope()));
				node->elseStatement->accept(wptr_this());
			leave_scope();
		}
	}
}

void FindSymbolVisitor::visit(wptr<WhileStatement> node) {
	assert(node);
	
	node->scope.set_automatically(current_scope());
	
	node->condition->accept(wptr_this());
	
	foreach (wptr<AssertionArea> inv, node->invariant)
		inv->accept(wptr_this());
	
	foreach (wptr<Expression> bnd, node->bound)
		bnd->accept(wptr_this());
	
	foreach (wptr<CompoundStatement> stmt, node->statement) {
		stmt->accept(wptr_this());
		if (node->bound.size())
			Symbol::construct_logical_variable(stmt->introducedScope(), "oldbound", parse<Type>(".int", current_scope()));
	}
}

void FindSymbolVisitor::visit(wptr<ForStatement> node) {
	assert(node);
	
	node->scope.set_automatically(current_scope());
	
	node->iteratorType->accept(wptr_this());
	node->iterator    ->accept(wptr_this());
	node->collection  ->accept(wptr_this());
	
	foreach (wptr<AssertionArea> inv, node->invariant) {
		if (typeid(*inv) == typeid(CompoundStatement)) {
			inv->accept(wptr_this());
		} else {
			enter_scope(Symbol::construct_scope(current_scope()));
				inv->accept(wptr_this());
			leave_scope();
		}
	}
	
	foreach (wptr<Statement> stmt, node->statement) {
		if (typeid(*stmt) == typeid(CompoundStatement)) {
			stmt->accept(wptr_this());
		} else {
			enter_scope(Symbol::construct_scope(current_scope()));
				node->iterator->symbol = Symbol::construct_iterator(current_scope(), node->iterator->text, node->iteratorType);
				
				stmt->accept(wptr_this());
			leave_scope();
		}
	}
}

void FindSymbolVisitor::visit(wptr<CompoundStatement> node) {
	assert(node);
	
	node->scope.set_automatically(current_scope());
	
	if (node->name) {
		node->name->accept(wptr_this());
		enter_scope(Symbol::construct_scope(current_scope(), node->name->text));
	} else {
		enter_scope(Symbol::construct_scope(current_scope()));
	}
		
		node->introducedScope.set_automatically(current_scope());
		current_scope()->compoundStatement = node;
		
		foreach (wptr<Statement> stmt, node->statements)
			stmt->accept(wptr_this());
		
	leave_scope();
}

void FindSymbolVisitor::visit(wptr<CppStatement> node) {
	assert(node);
	
	node->scope.set_automatically(current_scope());
	
	Navigator::visit(node);
}

void FindSymbolVisitor::visit(wptr<VariableDeclaration> node) {
	assert(node);
	
	node->scope.set_automatically(current_scope());
	
	node->type->accept(wptr_this());
	
	foreach (VariableDeclaration::Pair p, node->pairs) {
		p.name->accept(wptr_this());
		if (p.init)
			p.init->accept(wptr_this());
		
		p.name->symbol = Symbol::construct_variable(current_scope(), p.name->text, node->type);
	}
}

void FindSymbolVisitor::visit(wptr<TypeDeclaration> node) {
	assert(node);
	
	node->scope.set_automatically(current_scope());
	
	Navigator::visit(node);
	
	Symbol::construct_type(current_scope(), node->name->text, node);
}

void FindSymbolVisitor::visit(wptr<TypeType> node) {
	assert(node);
	
	node->scope.set_automatically(current_scope());
	
	Navigator::visit(node);
}

void FindSymbolVisitor::visit(wptr<FunctionDeclaration> node) {
	assert(node);
	
	node->scope.set_automatically(current_scope());
	
	node->name->accept(wptr_this());
	
	// Visit bounds
	//
	foreach (wptr<Expression> bound, node->bound)
		bound->accept(wptr_this());
	
	// Function symbol and scope
	//
	enter_scope(Symbol::construct_function(current_scope(), node->name->text, node->type, weak_vector(node->templateTypeNames), node));
		
		// Store the scope in which the actual parameters live.
		//
		node->paramScope.set_automatically(current_scope());
		
		// oldbound symbol
		//
		if (node->bound.size())
			Symbol::construct_logical_variable(current_scope(), "oldbound", parse<Type>(".int", current_scope()));
		
		// Parameter symbols and visit parameter types
		//
		foreach (FunctionDeclaration::Parameter param, node->parameters()) {
			param.type->accept(wptr_this());
			param.name->accept(wptr_this());
			Symbol::construct_variable(current_scope(), param.name->text, param.type);
		}
		
		// Template type symbols and visit template type identifiers
		//
		foreach (wptr<Identifier> ttn, node->templateTypeNames) {
			ttn->accept(wptr_this());
			ttn->symbol = Symbol::construct_type(current_scope(), ttn->text);
		}
		
		// Logical variable symbols and visit logical variable declarations
		//
		foreach (wptr<VariableDeclaration> lvars, node->logicalVars) {
			lvars->scope.set_automatically(current_scope());
			lvars->type->accept(wptr_this());
			foreach (VariableDeclaration::Pair lvar, lvars->pairs) {
				lvar.name->accept(wptr_this());
				if (lvar.init)
					lvar.init->accept(wptr_this());
				lvar.name->symbol = Symbol::construct_logical_variable(current_scope(), lvar.name->text, lvars->type);
			}
		}
		
		// Visit preconditions
		//
		foreach (wptr<AssertionArea> pre, node->pre)
			pre->accept(wptr_this());
		
		// Blinder symbol and scope
		//
		enter_scope(Symbol::construct_blinder(current_scope()));
			
			// Store the scope in which the result variable lives.
			//
			node->resultScope.set_automatically(current_scope());
			
			// Visit return-type.
			//
			node->type->returnType->accept(wptr_this());
			
			// `result' variable symbol
			//
			Symbol::construct_variable(current_scope(), "result", node->type->returnType);
			
			// Visit postconditions
			//
			foreach (wptr<AssertionArea> post, node->post)
				post->accept(wptr_this());
			
			// Body blinder symbols. No scopes, but the bodies are allowed to
			// contain clashing variable names, as long as they don't clash with
			// the names of `result', the parameters, logical variables and the
			// template types.
			//
			// This is only a temporary situation, as only one body is allowed
			// per function in the end. We allow multiple bodies right now
			// so we can report errors in each one.
			//
			foreach (wptr<CompoundStatement> body, node->body) {
				body->scope.set_automatically(current_scope());
				
				enter_scope(Symbol::construct_blinder(current_scope()));
					
					body->introducedScope.set_automatically(current_scope());
					current_scope()->compoundStatement = body;
					
					foreach (wptr<Statement> stmt, body->statements)
						stmt->accept(wptr_this());
					
				leave_scope();
			}
			
		leave_scope();
		
	leave_scope();
}

void FindSymbolVisitor::visit(wptr<FunctionCall> node) {
	assert(node);
	
	node->scope.set_automatically(current_scope());
	
	Navigator::visit(node);
}

void FindSymbolVisitor::visit(wptr<OperatorCall> node) {
	assert(node);
	
	node->scope.set_automatically(current_scope());
	
	Navigator::visit(node);
}

void FindSymbolVisitor::visit(wptr<Reference> node) {
	assert(node);
	
	node->scope.set_automatically(current_scope());
	
	Navigator::visit(node);
}

void FindSymbolVisitor::visit(wptr<BooleanLiteral> node) {
	assert(node);
	
	node->scope.set_automatically(current_scope());
	
	Navigator::visit(node);
}

void FindSymbolVisitor::visit(wptr<IntegerLiteral> node) {
	assert(node);
	
	node->scope.set_automatically(current_scope());
	
	Navigator::visit(node);
}

void FindSymbolVisitor::visit(wptr<ChainExpression> node) {
	assert(node);
	
	node->scope.set_automatically(current_scope());
	
	Navigator::visit(node);
}

void FindSymbolVisitor::visit(wptr<FunctionType> node) {
	assert(node);
	
	node->scope.set_automatically(current_scope());
	
	Navigator::visit(node);
}

void FindSymbolVisitor::visit(wptr<PointerType> node) {
	assert(node);
	
	node->scope.set_automatically(current_scope());
	
	Navigator::visit(node);
}

void FindSymbolVisitor::visit(wptr<ArrayType> node) {
	assert(node);
	
	node->scope.set_automatically(current_scope());
	
	Navigator::visit(node);
}

void FindSymbolVisitor::visit(wptr<NamedType> node) {
	assert(node);
	
	node->scope.set_automatically(current_scope());
	
	Navigator::visit(node);
}

void FindSymbolVisitor::visit(wptr<VoidType> node) {
	assert(node);
	
	node->scope.set_automatically(current_scope());
	
	Navigator::visit(node);
}

void FindSymbolVisitor::visit(wptr<Identifier> node) {
	assert(node);
	
	node->scope.set_automatically(current_scope());
	
	Navigator::visit(node);
}

void FindSymbolVisitor::enter_scope(wptr<Symbol> scope) {
	assert(scope);
	
	_currentScope = scope;
}

void FindSymbolVisitor::leave_scope() {
	assert(_currentScope);
	assert(_currentScope->parent);
	
	_currentScope = _currentScope->parent;
}

wptr<Symbol> FindSymbolVisitor::current_scope() {
	assert(_currentScope);
	
	return _currentScope;
}
